﻿using System;
using System.Collections.Generic;
using System.Linq;
using mqtttLib.Helper;
using mqtttLib.Messages.Base;

namespace mqtttLib.Messages
{
    /// <summary>
    /// 连接消息
    /// </summary>
    public sealed class ConnectMessage : MqttMessage
    {
        /// <summary>
        /// 协议名称
        /// </summary>
        public string ProtocolName { get; set; }
        /// <summary>
        /// 协议版本
        /// </summary>
        public byte ProtocolVersion { get; set; }

        #region 连接标志

        /// <summary>
        /// 遗嘱保留 置1服务端必须将遗嘱消息当作保留消息发布
        /// </summary>
        public bool WillRetain { get; private set; }

        private Qos _willQos;
        /// <summary>
        /// 遗嘱QoS
        /// </summary>
        public Qos WillQos
        {
            get { return _willQos; }
            private set
            {
                WillFlag = true;
                _willQos = value;
            }
        }
        /// <summary>
        /// 遗嘱标志 （ 设置为1，遗嘱QoS的值可以等于0(0x00)，1(0x01)，2(0x02)。它的值不能等于3 ； 设置为0，遗嘱QoS也必须设置为0(0x00)）
        /// </summary>
        public bool WillFlag { get; private set; }

        /// <summary>
        /// 用户名 标志
        /// </summary>
        public bool UsernameFlag { get; private set; }

        /// <summary>
        /// 密码标志
        /// </summary>
        public bool PasswordFlag { get; private set; }
        /// <summary>
        /// 清除会话 (长连接、短连接) 置1 客户端和服务端必须丢弃之前的任何会话并开始一个新的会话; 置0，服务端必须基于当前会话（使用客户端标识符识别）的状态恢复与客户端的通
        /// </summary>
        public bool CleanSession { get; set; }
        /// <summary>
        /// 保留标志
        /// </summary>
        public bool Reserved { get; set; }

        #endregion

        /// <summary>
        /// 保持连接（超时时间）（以秒为单位的时间间隔，16位的字）
        /// </summary>
        public short KeepAlive { get; set; }
        /// <summary>
        /// 客户端 Id
        /// </summary>
        public string ClientId { get; set; }
        /// <summary>
        /// 话题
        /// </summary>
        public string WillTopic { get; private set; }
        /// <summary>
        /// 遗嘱消息
        /// </summary>
        public string WillMessage { get; private set; }

        private string _userName;
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName
        {
            get { return _userName; }
            set
            {
                UsernameFlag = true;
                _userName = value;
            }
        }


        private string _password;
        /// <summary>
        /// 用户密码
        /// </summary>
        public string Password
        {
            get { return _password; }
            set
            {
                PasswordFlag = true;
                _password = value;
            }
        }

        public ConnectMessage(string clientId = "", bool d = true)
            : base(MessageType.CONNECT)
        {
            ProtocolName = "MQTT";
            ProtocolVersion = 0x04;
            ClientId = clientId;
            KeepAlive = 60;
        }

        /// <summary>
        /// 设置遗嘱
        /// </summary>
        /// <param name="willTopic"></param>
        /// <param name="willMessage">遗嘱消息</param>
        /// <param name="willQos">消息等级</param>
        /// <param name="willRetain"></param>
        /// <returns></returns>
        public bool SetWill(string willTopic, string willMessage, Qos willQos, bool willRetain)
        {
            WillFlag = true;
            WillTopic = willTopic;
            WillMessage = willMessage;
            WillQos = willQos;
            WillRetain = willRetain;
            return false;
        }



        public override byte[] GetDataBytes()
        {
            var result = new LinkedList<byte>();
            //variable header
            result.AddLast(ProtocolName);       //byte 1 - 8
            result.AddLast(ProtocolVersion);      //byte 9

            //连接标志;         //byte 10
            var flags = UsernameFlag.ToByte() << 7;
            flags |= PasswordFlag.ToByte() << 6;
            flags |= WillRetain.ToByte() << 5;
            flags |= ((byte)WillQos) << 3;
            flags |= WillFlag.ToByte() << 2;
            flags |= CleanSession.ToByte() << 1;
            flags |= Reserved.ToByte();
            result.AddLast((byte)flags);
            //超时时间
            result.AddLast(KeepAlive);      //byte 11 - 12 
            //有效载荷
            result.AddLast(ClientId);
            if (WillFlag)
            {
                result.AddLast(WillTopic);
                result.AddLast(WillMessage);
            }
            if (UsernameFlag)
                result.AddLast(UserName);
            if (PasswordFlag)
                result.AddLast(Password);

            FixedHeader.RemaingLength = result.Count;
            result.AddFirst(FixedHeader.GetDataBytes());
            return result.ToArray();


        }

        public override void GetDataObject(byte[] buffers)
        {
            int offset = GetStartOffset(buffers);  // int offset = buffers.Length - FixedHeader.RemaingLength;
            if (buffers.Length > 2)
            { 
                if (buffers.Length >= FixedHeader.RemaingLength)
                {
                    ProtocolName = buffers.ReadString(ref offset);
                    ProtocolVersion = buffers.Read(ref offset, 1)[0];
                    var flags = buffers.Read(ref offset, 1)[0];
                    UsernameFlag = (flags & (1 << 7)) == 0x80;
                    PasswordFlag = (flags & (1 << 6)) == 0x40;
                    WillRetain = (flags & (1 << 5)) == 0x20;
                    if ((flags & (1 << 4)) == 0x10)
                    {
                        if ((flags & (1 << 3)) == 0x08)
                            WillQos = Qos.ExactlyOnce;
                    }
                    else
                        WillQos = (Qos)(flags & (1 << 3)); 
                  //  byte[] d = buffers.Read(ref offset, 2);
                    KeepAlive = buffers.ReadShort(ref offset);
                    ClientId = buffers.ReadString(ref offset);

                    WillFlag = (flags & (1 << 2)) == 0x4;
                    CleanSession = (flags & (1 << 1)) == 0x2;
                    Reserved = (flags & (1 << 1)) == 0x1;


                    if (UsernameFlag)
                        UserName = buffers.ReadString(ref offset);
                    if (PasswordFlag)
                        Password = buffers.ReadString(ref offset);
                    if (WillFlag)
                    {
                        WillTopic = buffers.ReadString(ref offset);
                        WillMessage = buffers.ReadString(ref offset);
                    }
                }
            }
        }


    }

    /// <summary>
    /// 连接返回
    /// </summary>
    public sealed class ConnAckMessage : MqttMessage
    {
        /// <summary>
        /// 当前会话
        /// </summary>
        public bool SessionPresent { get; set; }
        /// <summary>
        /// 返回消息
        /// </summary>

        public MqttConnectReturnCode ReturnCode { get; set; }

        public ConnAckMessage()
            : base(MessageType.CONNACK)
        {
        }

        public override byte[] GetDataBytes()
        {

            var result = new LinkedList<byte>();
            byte flags = 0;
            flags |= SessionPresent.ToByte();
            result.AddLast(flags);
            result.AddLast((byte)ReturnCode);
            FixedHeader.RemaingLength = result.Count;
            result.AddFirst(FixedHeader.GetDataBytes());
            return result.ToArray();

        }

        public override void GetDataObject(byte[] buffers)
        {
            int offset = GetStartOffset(buffers);  // int offset = buffers.Length - FixedHeader.RemaingLength;
            SessionPresent = buffers.Read(ref offset, 1)[0] == 0x1;
            ReturnCode = (MqttConnectReturnCode)buffers.Read(ref offset, 1)[0];
        }
    }

    /// <summary>
    /// MQTT连接返回代码
    /// </summary>
    [Flags]
    public enum MqttConnectReturnCode : byte
    {
        /// <summary>
        /// 接受连接
        /// </summary>
        ConnectionAccepted = 0x00,
        /// <summary>
        /// 不支持的协议级别
        /// </summary>
        UnacceptedProtocolVersion = 0x01,
        /// <summary>
        /// 标识符的拒绝
        /// </summary>
        IdentifierRejected = 0x02,
        /// <summary>
        /// 经纪人不可用
        /// </summary>
        BrokerUnavailable = 0x03,
        /// <summary>
        /// 用户名或密码不正确
        /// </summary>
        BadUsernameOrPassword = 0x04,
        /// <summary>
        /// 没有权限
        /// </summary>
        NotAuthorized = 0x05
    }
}
